module(..., package.seeall)

import("utils.switch", "Switch")


----------------------------------------------------------------------------------------------------
-- Logger
----------------------------------------------------------------------------------------------------

--[[ Log manager to facilitate the management of multiple log streams. Each stream can write to a 
file of its own (or a common file object), allowing the application to produce various output files 
for different categories of log messages. 
--]]
Logger = class("Logger", Switch)

function Logger.__init(self, enabled)
    self.streams = list()
    self.info    = self:add_stream("Info",    io.stdout)
    self.debug   = self:add_stream("Debug",   io.stderr)
    self.warning = self:add_stream("Warning", io.stderr) 
    self.error   = self:add_stream("Error",   io.stderr)
    self.default = self.info
    self.state_stack = list()
    Switch.__init(self, (enabled == nil) and true or enabled)
end

function Logger.add_stream(self, name, out, enabled)
	local stream = LogStream(name, out, enabled)
	self.streams:append(stream)
	return stream
end

function Logger.__call(self, fmt, ...)
    return self.default(fmt, ...)
end

function Logger._enable(self)
    self:pop_state()
end

function Logger._disable(self)
    self:push_state()
    self:disable_all()
end

function Logger.push_state(self)
    local state = {}
    for stream in iter(self.streams) do 
        state[stream] = stream.enabled
    end
    self.state_stack:append(state)
end

function Logger.pop_state(self)
	if #self.state_stack > 0 then
	    for stream, enabled in pairs(self.state_stack:pop()) do
	        stream:set_enabled(enabled)
	    end
    end
end

function Logger.enable_all(self)
    for stream in iter(self.streams) do
        stream:enable()
    end
end

function Logger.disable_all(self)
    for stream in iter(self.streams) do
        stream:disable()
    end
end

----------------------------------------------------------------------------------------------------
-- LogStream
----------------------------------------------------------------------------------------------------

--[[ Class implementing a simple stream of log messages written to a file. Used by the Logger 
class, a slightly more complex log manager object containing multiple log streams.
--]]
LogStream = class("LogStream", Switch)

function LogStream.__init(self, name, out, enabled)
    self.name  = name
    self.out   = out or io.stdout
    self.write = no_write
    Switch.__init(self, (enabled == nil) and true or enabled)
end

function LogStream.__tostring(self)
    local status = self.enabled and "enabled" or "disabled"
    return ("<%s(%s, %s)>"):format(self.__name, self.name, status)
end

function LogStream.__call(self, fmt, ...)
    if self.enabled then
        self.write(self.out, log_line(self, fmt, ...))
    end
end
    
function LogStream._enable(self)
    self.write = self.out.write
end

function LogStream._disable(self)
    self.write = no_write
end

Logger.Stream = LogStream

----------------------------------------------------------------------------------------------------
-- Auxiliary functions
----------------------------------------------------------------------------------------------------
function log_line(stream, fmt, ...)
    local message
    if select("#", ...) == 0 then
        message = fmt
    else
        message = fmt:format(...)
    end
    return ("[%-10s] %s\n"):format(stream.name, message)
end

function no_write() end
